package com.brayan.melinxa.gui.systemtray;

import java.awt.AWTException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;

import javax.inject.Singleton;

import com.brayan.melinxa.Melinxa;
import com.brayan.melinxa.SystemStateObservable;
import com.brayan.melinxa.SystemStatus;
import com.brayan.melinxa.gui.windows.MainWindow;
import com.brayan.melinxa.gui.windows.WindowStateAndActionObservable;
import com.brayan.melinxa.logic.exceptions.ShadowerInitalisationException;

/**
 * The Tray Icon implementation.
 * 
 * @author Brayan Zimmerli
 */
@Singleton
public class Tray implements SystemStateObservable, WindowStateAndActionObservable {

	Melinxa melinxa = null;
	MainWindow window = null;

	SystemTray systemTray = null;
	TrayIcon trayIcon = null;

	PopupMenu popup;
	MenuItem openMenuItem;
	MenuItem activateMenuItem;

	private SystemStatus state = null;
	private boolean isActivated;
	private boolean isWindowOpened = false;

	private final String MENU_TEXT_WHEN_ACTIVATED = "Deactivate data shadowing";
	private final String MENU_TEXT_WHEN_DEACTIVATED = "Activate data shadowing";
	private final String MENU_TEXT_WHEN_CLOSED = "No shadower employed. Configure a shadower first.";

	private final String MENU_OPEN = "Open Melinxa";
	private final String MENU_MINIMIZE = "Hide Melinxa";

	private final String TRAY_ICON_TOOL_TIP_ACTIVATED = "Melinxa - data shadowing activated";
	private final String TRAY_ICON_TOOL_TIP_DEACTIVATED = "Melinxa - data shadowing is inactive";
	private final String TRAY_ICON_TOOL_TIP_CLOSED = "Melinxa - data shadowing has been shut down.";
	private final String TRAY_ICON_TOOL_TIP_ERROR = "Melinxa - data shadowing experience an error. The error has been submitted.";

	public Tray(Melinxa melinxa, MainWindow window) {
		this.melinxa = melinxa;
		registerAsMelinxaStateObserver();
		this.window = window;
		init();
	}

	/**
	 * Initialise the graphics of the tray icon.
	 */
	private void init() {
		this.window.registerWindowStateObserver(this);
		if (SystemTray.isSupported()) {
			// get the SystemTray instance
			systemTray = SystemTray.getSystemTray();

			ActionListener listenerIcon = new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					System.out.println("icon action");
				}
			};

			ActionListener listenerOpen = new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					toggleFrameVisibility();
				}
			};

			ActionListener listenerActivate = new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					activateShadowing(!isActivated());
				}
			};

			// Create a popup menu.
			popup = new PopupMenu();
			// construct a TrayIcon
			URL imgURL = ClassLoader.getSystemResource("media/Suivant-Natural-256.png");
			Image image = Toolkit.getDefaultToolkit().getImage(imgURL);
			trayIcon = new TrayIcon(image, MENU_TEXT_WHEN_ACTIVATED, popup);

			// Open the gui application.
			openMenuItem = new MenuItem(MENU_OPEN);
			openMenuItem.addActionListener(listenerOpen);
			popup.add(openMenuItem);
			popup.addSeparator();

			activateMenuItem = new MenuItem(MENU_TEXT_WHEN_DEACTIVATED);
			activateMenuItem.addActionListener(listenerActivate);
			popup.add(activateMenuItem);

			// set the TrayIcon properties
			trayIcon.addActionListener(listenerIcon);
			trayIcon.setImageAutoSize(true);

			// Activate or deactivate the data shadowing.
			if (melinxa.isMelinxaReady())
				setState(SystemStatus.ACTIVATED);
			else
				setState(SystemStatus.DEACTIVATED);

			try {
				systemTray.add(trayIcon);
			} catch (AWTException e) {
				// TODO user logger
				System.err.println(e);
			}
			// ...
		} else {
			// TODO use logger
			System.out.println("Traybar is not supported on your operating system.");
		}
	}

	private void toggleFrameVisibility() {
		// TODO Implement a listener/observer on the window!
		window.showWindow(!isWindowOpened);
	}

	private Image getIconImage() {
		String imageStr = null;
		if (getState() == SystemStatus.ACTIVATED)
			imageStr = "media/Suivant-Natural-256.png";
		else if (getState() == SystemStatus.DEACTIVATED)
			imageStr = "media/Suivant-Sky-256.png";
		else
			imageStr = "media/Suivant-Rouge-256.png";

		URL imgURL = ClassLoader.getSystemResource(imageStr);
		return Toolkit.getDefaultToolkit().getImage(imgURL);
	}

	private boolean isActivated() {
		return isActivated;
	}

	public void setActivated(boolean isActivated) {
		if (isActivated)
			setState(SystemStatus.ACTIVATED);
		else
			setState(SystemStatus.DEACTIVATED);
		this.isActivated = isActivated;
	}

	public SystemStatus getState() {
		return state;
	}

	public void setState(SystemStatus state) {
		this.state = state;
		if (state == SystemStatus.ACTIVATED)
			this.isActivated = true;
		else if (state == SystemStatus.DEACTIVATED)
			this.isActivated = false;
		else if (state == SystemStatus.ERROR)
			this.isActivated = false;
		else if (state == SystemStatus.CLOSED) {
			this.isActivated = false;
			close();
		}
		setGraphElementsStatus(state);
	}

	/**
	 * Set the graphicals active, if the the shadowing is actived. It's a mapper
	 * for {@link #setGraphElementsStatus(ShadowerStatus)}.
	 * 
	 * @param isShadowingActivated
	 *            Set to true, if the shadowing is currently activated
	 */
	private void setGraphElementsToActivated(boolean activated) {
		if (activated)
			setGraphElementsStatus(SystemStatus.ACTIVATED);
		else
			setGraphElementsStatus(SystemStatus.DEACTIVATED);
	}

	private void setGraphElementsStatus(SystemStatus state) {
		if (state == SystemStatus.ACTIVATED) {
			trayIcon.setImage(getIconImage());
			activateMenuItem.setLabel(MENU_TEXT_WHEN_ACTIVATED);
			trayIcon.setToolTip(TRAY_ICON_TOOL_TIP_ACTIVATED);
		} else if (state == SystemStatus.DEACTIVATED) {
			trayIcon.setImage(getIconImage());
			activateMenuItem.setLabel(MENU_TEXT_WHEN_DEACTIVATED);
			trayIcon.setToolTip(TRAY_ICON_TOOL_TIP_DEACTIVATED);
		} else if (state == SystemStatus.CLOSED) {
			trayIcon.setImage(getIconImage());
			activateMenuItem.setLabel(MENU_TEXT_WHEN_CLOSED);
			trayIcon.setToolTip(TRAY_ICON_TOOL_TIP_CLOSED);
		} else {
			trayIcon.setImage(getIconImage());
			activateMenuItem.setLabel(MENU_TEXT_WHEN_DEACTIVATED);
			trayIcon.setToolTip(TRAY_ICON_TOOL_TIP_ERROR);
		}
	}

	private void activateShadowing(boolean activate) {

		if (!melinxa.isMelinxaReady()) {
			// TODO use logger
			System.out
					.println("Melinxa is not ready. Cannot activate. Shadower might not be ready.");
			return;
		}
		try {
			// reinit the Melinxa app.
			melinxa.getShadower().activate(activate);
			// isActivated = activate;
			setActivated(activate);
			setGraphElementsToActivated(activate);
		} catch (ShadowerInitalisationException e) {
			// TODO use loggger
			System.out.println("Failed to activate Shadowing.");
			System.out.println(e.getMessage());
		}
	}

	/**
	 * Close the system tray and dispose.
	 */
	public void close() {
		if (systemTray != null)
			systemTray.remove(trayIcon);
	}

	public void windowStateChangedToOpened(boolean open) {
		if (openMenuItem != null) {
			openMenuItem.setLabel(open ? MENU_MINIMIZE : MENU_OPEN);
			isWindowOpened = open;
		}
	}

	public void registerAsMelinxaStateObserver() {
		if (melinxa != null)
			melinxa.registerSystemStateObserver(this);
		else {
			// TODO use logger
			System.out
					.println("Cannot register Tray with the shadower for events, because the shadower is not initalized.");
		}
	}

	@Override
	public void systemStateChangedTo(SystemStatus state) {
		setState(state);
		setGraphElementsStatus(state);
	}

}
